/******************************************************************************
* This file is part of is32p218 platform,
* Copyright (c) 2009 HangZhou InfoStrong CO.,LTD. All rights reserved.
* This software may only be used under the terms of a valid, current,
* end user license from InfoStrong.
* Nothing else gives you the right to use this software.   
*
* Name:   drv_uart.c
* Desc:   
* Author: Jerry
* Date:   2009-9-15
* Note:   
* History:
*
******************************************************************************/
#include "def.h"
#include "option.h"
#include "xn5106x.h"
#include "drv_sys.h"
#include "drv_uart.h"
#include "drv_int.h"
#include "os_api.h"
#include "mw_nram.h"
#include "sys_glb.h"
#include "app_afn.h"
#include "drv_tmr.h"
#include "host_if.h"

DrvUARTCtrl g_drv_uart_ctrl;

const U16 c_drv_uart_baud_list[]=
{
	UART_BAUD_600,
	UART_BAUD_1200,  
	UART_BAUD_2400, 
	UART_BAUD_4800, 
	UART_BAUD_9600, 
	UART_BAUD_19200, 
	UART_BAUD_38400,
	UART_BAUD_57600,
};

extern BOOL host_if_msg_snd( U8 msg_id, U8 p_msg_data );
#ifdef SIMU_UART

#define DRV_U2CON_PARITY_NO		0x00
#define DRV_U2CON_PARITY_ODD	0x01
#define DRV_U2CON_PARITY_EVEN	0x02
#define DRV_U2CON_PARITY_MARK	0x03
#define DRV_U2CON_PARITY_SPACE	0x04

#define DRV_U2CON_STOP_ONE		0x00
#define DRV_U2CON_STOP_TWO		0x04

#pragma O2
#pragma OTime

extern U8 drv_uart2_fifo_pop( void );
extern void drv_uart2_int_clr( void );
extern U8 drv_uart2_fifo_empty( void );
extern void drv_uart2_hw_rx_ena( BOOL rx_flg );
extern BOOL drv_uart2_hw_init( U16 baud, S32 offset, U8 Con );
/*
* Function Name:
*   isr_uart2_rx
* Description:
*   UART2 IRQ interrupt ISR
* Parameters:
* Returns:
*   :
* Author				Date 
*  Jerry				2011-9-26   
* Note:
*    
*/
INT_FUNC isr_uart2_rx( void )		// HandlerT4OVF
{
	U8 rec_byte;
	while (!drv_uart2_fifo_empty())
	{	
		rec_byte = drv_uart2_fifo_pop();
		/* Send Char to Queue */
		//if ( pdPASS == isOS_QueueSend( &g_drv_uart_ctrl.m_rx_que, &rec_byte, 0 ) )
		if( host_if_msg_snd(MSG_UART_TO_HOST_IF_DATA,rec_byte) )
		{
			drv_uart_busy_set( TRUE );
		}
	}

	drv_uart2_int_clr();
}

#pragma O1

/*
* Function Name:
*   drv_uart2_init
* Description:
*   Sim-Uart init
* Parameters:
*   U16 baud: baud2 should use UART_BAUD2_xxxx
*   S32 offset: sample position offset( <0: pre-standard, >0: post-standard ), unit (us)
* Returns:
*   BOOL:
* Author				Date 
*  Jerry				2011-9-21   
* Note:
*    Hardware resouce: EINT1+TIMER4+BT
*/
BOOL drv_uart2_init( U16 baud, S32 offset )
{
	// Data: 8bits; STOP: 1bit; Parity: Even; Mode: Normal
	drv_uart2_hw_init(baud,offset,DRV_U2CON_PARITY_EVEN|DRV_U2CON_STOP_ONE);
	// UART2 IRQ interrupt ISR. Use BT interrupt number
	drv_int_isr_set( INT_NUM_BT, isr_uart2_rx );
	drv_int_ena( INT_NUM_BT, FALSE );

	return TRUE;
}

/*
* Function Name:
*   drv_uart2_rx_ena
* Description:
*   UART2 RX Enable/Disable
* Parameters:
*   BOOL rx_flg: TRUE--Enable; FALSE--Disable
* Returns:
*   :
* Author				Date 
*  Jerry				2011-9-27   
* Note:
*    
*/
void drv_uart2_rx_ena( BOOL rx_flg )
{
	drv_uart2_hw_rx_ena( rx_flg );
}


/*
* Function Name:
*   drv_uart2_send_byte
* Description:
*   UART2 send byte 
* Parameters:
*   U8 data: data
* Returns:
*   BOOL:
* Author				Date 
*  Jerry				2011-9-27   
* Note:
*    Current, this feature is not realized
*/
BOOL drv_uart2_send_byte(U8 data)
{
	return FALSE;
}

#endif	// end DRV_UART2_FLG

/****************************************************************************** 
                     Implementation
*******************************************************************************/
/*
* Function Name:
*   drv_uart_int_proc
* Description:
*   UART interrupt procedure
* Parameters:
* Returns:
* Author				Date 
*  Jerry				2010-1-29   
* Note:
*    
*/
void drv_uart_int_proc( void )
{
	U8 status;
	U8 rx_flg;
	U8 tx_flg;
	U8 err_flg;
	
	status=(rUSSR&0xFF);
	rx_flg = (status&USSR_RX);
	tx_flg = (status&USSR_TX)&&(rINTPEND&BIT_UTXD);
	err_flg = (status&(USSR_ERR_OVERRUN|USSR_ERR_PARITY|USSR_ERR_FRAME));

	while ( (rx_flg|tx_flg)||err_flg )
	{
		if ( rx_flg||err_flg )
		{
			U8 rec_byte;
			if ( err_flg )
			{
				rINTPEND = ~BIT_UERR;
			}

			rec_byte = rRBR;
			rINTPEND = ~BIT_URXD;		// RXD interrupt flag should be cleared, in all rx proc( include: rx and err )
		#ifndef SIMU_RX_FLG
			/* Send Char to Queue */
			//if ( pdPASS == isOS_QueueSend( &g_drv_uart_ctrl.m_rx_que, &rec_byte, 0 ) )
			if( host_if_msg_snd(MSG_UART_TO_HOST_IF_DATA,rec_byte) )
			{
				drv_uart_busy_set( TRUE );
			}
		#else
			rec_byte = rec_byte;
		#endif
		}
		if ( tx_flg )
		{
			rINTPEND = ~BIT_UTXD;
		#if UART_TX_QUE_FLG
					/* Get TX Queue, If not empty, send next char, otherwise, should disable tx interrupt */
			if ( isOS_QueueIsEmpty( &g_drv_uart_ctrl.m_tx_que ) )
			{
				g_drv_uart_ctrl.m_tx_int_flg = TRUE;
				// drv_int_ena( INT_NUM_UTXD, FALSE );
			}
			else
			{	/* Send next byte to UART */
				U8 snd_byte;
				U32 ret;
				ret = isOS_QueueReceive( &g_drv_uart_ctrl.m_tx_que, &snd_byte, 0);
				if ( pdPASS == ret )
				{
					rTBR = snd_byte;
				}
			}
		#else
			g_drv_uart_ctrl.m_tx_ok_flg = TRUE;
		#endif
		}
		status = (rUSSR&0xFF);
		rx_flg = (status&USSR_RX);
		tx_flg = (status&USSR_TX)&&(rINTPEND&BIT_UTXD);
		err_flg = (status&(USSR_ERR_OVERRUN|USSR_ERR_PARITY|USSR_ERR_FRAME));
	}

}
/*
* Function Name:
*   isr_uart_rx
* Description:
*   UART RX ISR
* Parameters:
* Returns:
*   INT_FUNC
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
INT_FUNC isr_uart_rx( void )
{
	drv_uart_int_proc();
}

/*
* Function Name:
*   isr_uart_tx
* Description:
*   UART ISR
* Parameters:
* Returns:
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
INT_FUNC isr_uart_tx(void)
{
	drv_uart_int_proc();
}

/*
* Function Name:
*   isr_uart_tx
* Description:
*   UART ISR
* Parameters:
* Returns:
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
INT_FUNC isr_uart_err(void)
{
	drv_uart_int_proc();
	rINTPEND = ~BIT_UERR;
}


/*
* Function Name:
*   drv_uart_init
* Description:
*   UART module init
* Parameters:
*  	U16 baud:
*      #define UART_BAUD_600       1249
*      #define UART_BAUD_1200      624 
*      #define UART_BAUD_2400      312 
*      #define UART_BAUD_4800      155 
*      #define UART_BAUD_9600      77  
*      #define UART_BAUD_19200     38  
*      #define UART_BAUD_38400     19  
*      #define UART_BAUD_57600     12  
*  
* Returns:
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
void drv_uart_init(U16 baud)
{
	isOS_S32 ret;

#ifdef SIMU_UART
	drv_uart2_init( baud, 0 );
#endif

//	ret = isOS_QueueCreate( &g_drv_uart_ctrl.m_rx_que, g_drv_uart_ctrl.m_rx_buff, \
							DRV_UART_RX_BUFF_LEN, DRV_UART_ITEM_SIZE );
	ret = isOS_QueueCreate( &g_drv_uart_ctrl.m_rx_que, g_drv_uart_ctrl.m_rx_buff, \
							DRV_UART_RX_BUFF_LEN, sizeof(MSG_HDR_HOST_T) );
	if ( ret != pdPASS )
	{
		IS_ASSERT(0);
	}
#if UART_TX_QUE_FLG
//	ret = isOS_QueueCreate( &g_drv_uart_ctrl.m_tx_que, g_drv_uart_ctrl.m_tx_buff, \
							DRV_UART_TX_BUFF_LEN, DRV_UART_ITEM_SIZE );
	ret = isOS_QueueCreate( &g_drv_uart_ctrl.m_tx_que, g_drv_uart_ctrl.m_tx_buff, \
							DRV_UART_TX_BUFF_LEN, sizeof(MSG_HDR_HOST_T) );
	if ( ret != pdPASS )
	{
		IS_ASSERT(0);
	}
	g_drv_uart_ctrl.m_tx_int_flg = TRUE;
#else
	g_drv_uart_ctrl.m_tx_ok_flg = FALSE;
#endif

	ret = isOS_SemCreate( &g_drv_uart_ctrl.m_sem, 1, 1, "UART Sem");
	if ( ret != pdPASS )
	{
		IS_ASSERT(0);
	}
	
	drv_uart_status_reset();
	// rUBRDR=( (int)(EXTCLK_FREQ/(8*baud) + 1)/2-1 );
	rUBRDR = baud;
	g_drv_uart_ctrl.m_baud = baud;

	drv_uart_int_init();
}

/*
* Function Name:
*   drv_uart_status_check
* Description:
*   
* Parameters:
*   BOOL
* Returns:
* Author				Date 
*  feye					2010-6-2   
* Note:
*    
*/
BOOL drv_uart_status_check(void)
{
	if((rSYSCON&BIT7)||(0x6B!=rLCON)||
		((UCON_RX_INT_EN|UCON_RX_STATUS_INT_EN|UCON_TX_INT_EN)!=rUCON))
	{
		return FALSE;
	}
	return TRUE;
}


/*
* Function Name:
*   drv_uart_status_reset
* Description:
*   
* Parameters:
*   BOOL
* Returns:
* Author				Date 
*  feye					2010-6-2   
* Note:
*    
*/
void drv_uart_status_reset(void)
{
	U8 by_tmp;
	rSYSCON &= (~BIT7);	// Enable UART clock
	rLCON = 0x6B;		// Data: 8bits; STOP: 1bit; Parity: Even; Mode: Normal
	rUCON = UCON_RX_INT_EN|UCON_RX_STATUS_INT_EN|UCON_TX_INT_EN;// NO_LPBK NO_BR 0 01 NO_STINT 01
	by_tmp = rUSSR;		// Clear UART status register
	by_tmp = rUSSR;
	by_tmp = by_tmp;
}


/*
* Function Name:
*   drv_uart_baud_check
* Description:
*   
* Parameters:
*   BOOL
* Returns:
* Author				Date 
*  feye					2010-6-2   
* Note:
*    
*/
BOOL drv_uart_baud_check(void)
{
#ifdef BAUD_SAVE	
	U16 baud_tmp;

	if ( !bu_mw_Nram_read( NRAM_ITEM_UART_BAUD, 0, sizeof(baud_tmp), (U8*)&baud_tmp ) \
		 || (baud_tmp <UART_BAUD_57600|| baud_tmp>UART_BAUD_600) )
	{
		baud_tmp=UART_BAUD_DEFAULT;//use default baud
		if ( !bu_mw_Nram_write( NRAM_ITEM_UART_BAUD, 0, (U8*)&baud_tmp, sizeof(baud_tmp) ) )
			{
				IS_ASSERT(0);
			}
	}
	if(baud_tmp == rUBRDR)
	{
		return TRUE;
	}
#else	
	U8 idx;
	U16 cur_brdr;
	U32 cpu_sr;


	isOS_ENTER_CRITICAL(cpu_sr);
	cur_brdr = rUBRDR;

	if ( cur_brdr != g_drv_uart_ctrl.m_baud )
	{
		isOS_EXIT_CRITICAL(cpu_sr);
		return FALSE;
	}
	isOS_EXIT_CRITICAL(cpu_sr);

	for ( idx=0; idx<sizeof(c_drv_uart_baud_list)/sizeof(U16); idx++ )
	{
		if ( c_drv_uart_baud_list[idx] == cur_brdr )
		{
			return TRUE;
		}
	}
#endif
	return FALSE;
	
}
/*
* Function Name:
*   drv_uart_baud_resume
* Description:
*   
* Parameters:
*   BOOL
* Returns:
* Author				Date 
*  feye					2010-6-2   
* Note:
*    
*/

void drv_uart_baud_resume(void)
{	
#ifdef BAUD_SAVE	
	U16 baud_tmp;

	if ( !bu_mw_Nram_read( NRAM_ITEM_UART_BAUD, 0, sizeof(baud_tmp), (U8*)&baud_tmp ) \
		 || (baud_tmp <UART_BAUD_57600|| baud_tmp>UART_BAUD_600) )
	{
		baud_tmp=UART_BAUD_DEFAULT;//use default baud
		if ( !bu_mw_Nram_write( NRAM_ITEM_UART_BAUD, 0, (U8*)&baud_tmp, sizeof(baud_tmp) ) )
			{
				IS_ASSERT(0);
			}
	}

	drv_uart_baud_set( baud_tmp );
#else
	drv_uart_baud_set( UART_BAUD_DEFAULT );
#endif	
}

/*
* Function Name:
*   drv_uart_baud_set
* Description:
*   Baud set
* Parameters:
*  	U16 baud:
*       #define UART_BAUD_600       1249
*       #define UART_BAUD_1200      624 
*       #define UART_BAUD_2400      312 
*       #define UART_BAUD_4800      155 
*       #define UART_BAUD_9600      77  
*       #define UART_BAUD_19200     38  
*       #define UART_BAUD_38400     19  
*       #define UART_BAUD_57600     12  
*  
* Returns:
* Author				Date 
*  Jerry				2010-4-13   
* Note:
*    
*/
void drv_uart_baud_set( U16 baud )
{
	rUBRDR = baud;
#if defined(SIMU_RX_FLG)&&defined(SIMU_UART)
	drv_uart2_init( baud, 0 );
#endif
	g_drv_uart_ctrl.m_baud = baud;
}



/*
* Function Name:
*   drv_uart_int_init
* Description:
*   
* Parameters:
* Returns:
* Author				Date 
*  jerry				2009-9-24   
* Note:
*    
*/

void drv_uart_int_init( void )
{
	//drv_int_mode_set( INT_NUM_URXD, 1 );		// FIQ mode
	drv_int_isr_set( INT_NUM_URXD, isr_uart_rx );
	drv_int_isr_set( INT_NUM_UTXD, isr_uart_tx );
	drv_int_isr_set( INT_NUM_UERR, isr_uart_err );
	drv_int_ena( INT_NUM_UTXD, TRUE );
#ifndef SIMU_RX_FLG
	drv_int_ena( INT_NUM_URXD, TRUE );
	drv_int_ena( INT_NUM_UERR, TRUE );
#endif
}

/*
* Function Name:
*   drv_uart_busy_set
* Description:
*   
* Parameters:
*   BOOL busy_flg:
* Returns:
* Author				Date 
*  Jerry				2009-11-26   
* Note:
*    
*/
static S32 g_busy_cnt = 0;
void drv_uart_busy_set( BOOL busy_flg )
{
#if 0
	// --- Set one PIN to indicat Busy
	if (busy_flg)
	{
		if ( 0 == g_busy_cnt )
		{
			drv_int_ena( INT_NUM_URXD, FALSE );
		}
		g_busy_cnt++;
	}
	else
	{
		if ( g_busy_cnt )
		{
			g_busy_cnt--;
			if ( 0== g_busy_cnt)
			{
				drv_int_ena( INT_NUM_URXD, TRUE );
			}
		}
	}
#else
	g_busy_cnt++;
#endif
}

/*
* Function Name:
*   drv_uart_que_get
* Description:
*   UART Queue get
* Parameters:
*  	DRV_UART_QUE_TYPE mode:
*  			DRV_UART_QUE_TX: tx queue,
* 			DRV_UART_QUE_RX: rx queue
* Returns:
*   isOS_QUE*:
* Author				Date 
*  Jerry				2010-1-28   
* Note:
*    
*/
isOS_QUE *drv_uart_que_get( DRV_UART_QUE_TYPE mode )
{
	isOS_QUE *p_queue;
	if (DRV_UART_QUE_RX == mode)
	{
		p_queue = &g_drv_uart_ctrl.m_rx_que;
	}
	else
	{
#if UART_TX_QUE_FLG
		p_queue = &g_drv_uart_ctrl.m_tx_que;
#else
		p_queue = NULL;
#endif
	}
	return p_queue;
}

/*
* Function Name:
*   drv_uart_rec_byte
* Description:
*   Receiver one byte from UART
* Parameters:
* Returns:
*   char: Received byte
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
char drv_uart_rec_byte(void)
{
	while (!(rUSSR & 0x20)); //Receive data read
	return rRBR;
}

/*
* Function Name:
*   drv_uart_send_byte
* Description:
*   
* Parameters:
*   U8 data:
* Returns: 
*   BOOL 
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
BOOL drv_uart_send_byte(U8 data)
{
#if (0 == UART_TX_QUE_FLG)
	U32 loop;
	g_drv_uart_ctrl.m_tx_ok_flg = FALSE;
	rTBR=(U8)data;
	//Wait until THR is empty.
	loop = (rUBRDR+5)*40;		// (6*2000)/4000ms = 3ms( 9600bps, need 1ms )
	while (FALSE == g_drv_uart_ctrl.m_tx_ok_flg&&loop)	
	{
		loop--;
	}
	return g_drv_uart_ctrl.m_tx_ok_flg;
#else
	return FALSE;
#endif
}
/*
* Function Name:
*   drv_uart_send_shift_ok
* Description:
*   Wait for UART send shift ok
* Parameters:
* Returns:
*   BOOL:
* Author				Date 
*  Jerry				2012-5-24   
* Note:
*    
*/
BOOL drv_uart_send_shift_ok( void )
{
	U32 loop;
	//Wait until THR is empty.
	loop = (rUBRDR)*26;		// (6*2000)/4000ms = 3ms( 9600bps, need 1ms )

	while ( (0==(rUSSR&USSR_TX_SHIFT))&&loop)	
	{
		loop--;
	}
	return TRUE;
}

/*
* Function Name:
*   drv_uart_send_byte2
* Description:
*   UART Send byte, use status loop
* Parameters:
*   U8 data:
* Returns: 
*   BOOL 
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
BOOL drv_uart_send_byte2(U8 data)
{
#if (0 == UART_TX_QUE_FLG)
	U32 loop;
	rTBR=(U8)data;
	//Wait until THR is empty.
	loop = (rUBRDR+1)*26;		// (6*2000)/4000ms = 3ms( 9600bps, need 1ms )
	while ((!(rUSSR&USSR_TX))&&loop)	
	{
		loop--;
	}
	return (loop)?TRUE:FALSE;
#else
	return FALSE;
#endif
}


/*
* Function Name:
*   drv_uart_send_data
* Description:
*   Send data to Queue, and start Uart interrupt
* Parameters:
*   U8* p_snd_data:
*   U32 snd_len:
* Returns:
*   BOOL: 1--Success, 0--Failure
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*    
*/
BOOL drv_uart_send( U8 *p_snd_data, U32 snd_len, BOOL blk_flg )
{
	isOS_S32 ret;
	ret = isOS_SemAcquire( &g_drv_uart_ctrl.m_sem, TM_WAIT_FOREVER );
	if ( ret != pdPASS )
	{
		IS_ASSERT(0);
	}

#if UART_TX_QUE_FLG
	while( snd_len-- )
	{
		isOS_QueueSend( &g_drv_uart_ctrl.m_tx_que, p_snd_data, TM_POLLING );
		p_snd_data++;
	}

	//if ( !drv_int_stat(INT_NUM_UTXD) )
	if ( g_drv_uart_ctrl.m_tx_int_flg )
	{
		U8 snd_byte;
		U32 ret;
		ret = isOS_QueueReceive( &g_drv_uart_ctrl.m_tx_que, &snd_byte, 0 );
		
		if ( pdPASS == ret )
		{
			g_drv_uart_ctrl.m_tx_int_flg = FALSE;
			//drv_int_ena( INT_NUM_UTXD, TRUE );
			rINTPEND=~BIT_UTXD;
			rTBR = snd_byte;
		}
	}
#else
	while( snd_len-- )
	{
		if ( FALSE == drv_uart_send_byte( *p_snd_data++ ) )
		{
			ret = isOS_SemSignal( &g_drv_uart_ctrl.m_sem );
			if ( ret != pdPASS )
			{
				IS_ASSERT(0);
			}

			return FALSE;
		}
	}
#endif
	ret = isOS_SemSignal( &g_drv_uart_ctrl.m_sem );
	if ( ret != pdPASS )
	{
		IS_ASSERT(0);
	}

	return TRUE;
}

/*
* Function Name:
*   drv_uart_send_data
* Description:
*   Send data to UART, block mode( no interrupt )
* Parameters:
*   U8* p_snd_data:
*   U32 snd_len:
* Returns:
*   BOOL: 1--Success, 0--Failure
* Author				Date 
*  Jerry				2009-9-15   
* Note:
*  Only called by interrupt  
*/
BOOL drv_uart_send2( U8 *p_snd_data, U32 snd_len, BOOL blk_flg )
{
//	isOS_S32 ret;
	// Disable UART interrupt
	drv_int_ena( INT_NUM_URXD, FALSE );
	drv_int_ena( INT_NUM_UTXD, FALSE );
	while( snd_len-- )
	{
		if ( FALSE == drv_uart_send_byte2( *p_snd_data++ ) )
		{
			return FALSE;
		}
	}

	return TRUE;
}



